﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LevelEditor.controller;
using LevelEditor.events;

namespace LevelEditor
{
    public partial class EditorView : Form, ILevelEditorListener
    {
        #region Variables
        private EditorController m_editorController;
        private int mouseCurrentX, mouseCurrentY;
        private PictureBox currentPictureBox;
        private bool isDragging;
        private bool m_isCtrlKeyPressed;
        private List<List<PictureBox>> doublePages;
        private int currentDoublePage;
        private int m_currentPictureBoxDoublePageIndex;
        private string currentLevelSaveUrl;

        #endregion

        #region Constructeurs
        public EditorView( EditorController editorController )
        {
            m_editorController = editorController;

            InitializeComponent();

            this.button1.MouseClick += new MouseEventHandler(this.button1_MouseClick);

            this.KeyPreview = true;
            this.KeyDown += new KeyEventHandler(this.myKeyDown);
            this.KeyUp += new KeyEventHandler(this.myKeyUp);

            this.isDragging = false;
            m_isCtrlKeyPressed = false;
            currentPictureBox = null;

            currentDoublePage = 0;
            m_currentPictureBoxDoublePageIndex = 0;
            doublePages = new List<List<PictureBox>>();
            currentLevelSaveUrl = null;

            this.panel1.Image = Image.FromFile("../../assets/background.jpg");
            this.panel1.SizeMode = PictureBoxSizeMode.StretchImage;
            this.panel1.SendToBack();

            this.panel1.MouseUp += new MouseEventHandler(this.myMouseUp);
            this.panel1.MouseLeave += new EventHandler(this.myMouseLeave);
            this.panel1.MouseDown += new MouseEventHandler(this.panel1_MouseDown);
        }

        #endregion

        #region Keyboard and Mouse events

        //Rotate image
        public static Bitmap RotateImg(Bitmap bmp, float angle, Color bkColor)
        {
            int w = bmp.Width;
            int h = bmp.Height;
            PixelFormat pf = default(PixelFormat);
            if (bkColor == Color.Transparent)
            {
                pf = PixelFormat.Format32bppArgb;
            }
            else
            {
                pf = bmp.PixelFormat;
            }

            Bitmap tempImg = new Bitmap(w, h, pf);
            Graphics g = Graphics.FromImage(tempImg);
            g.Clear(bkColor);
            g.DrawImageUnscaled(bmp, 1, 1);
            g.Dispose();

            GraphicsPath path = new GraphicsPath();
            path.AddRectangle(new RectangleF(0f, 0f, w, h));
            Matrix mtrx = new Matrix();
            //Using System.Drawing.Drawing2D.Matrix class 
            mtrx.Rotate(angle);
            RectangleF rct = path.GetBounds(mtrx);
            Bitmap newImg = new Bitmap(Convert.ToInt32(rct.Width), Convert.ToInt32(rct.Height), pf);
            g = Graphics.FromImage(newImg);
            g.Clear(bkColor);
            g.TranslateTransform(-rct.X, -rct.Y);
            g.RotateTransform(angle);
            g.InterpolationMode = InterpolationMode.HighQualityBilinear;
            g.DrawImageUnscaled(tempImg, 0, 0);
            g.Dispose();
            tempImg.Dispose();
            return newImg;
        }

        // Click bouton "add to scene"
        private void button1_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (currentPictureBox != null)
                currentPictureBox.BorderStyle = BorderStyle.None;

            PictureBox picbox = new PictureBox();
            picbox.Height = pictureBox1.Image.Height;
            picbox.Width = pictureBox1.Image.Width;
            picbox.Image = pictureBox1.Image;
            picbox.SizeMode = PictureBoxSizeMode.AutoSize;
            picbox.BackColor = Color.Transparent;
            picbox.Cursor = Cursors.Hand;
            picbox.BorderStyle = BorderStyle.FixedSingle;

            picbox.MouseDown += new MouseEventHandler(this.pictureBox_MouseDown);
            picbox.MouseMove += new MouseEventHandler(this.pictureBox_MouseMove);
            picbox.MouseUp += new MouseEventHandler(this.myMouseUp);
            picbox.MouseLeave += new EventHandler(this.myMouseLeave);

            doublePages[currentDoublePage].Add(picbox);
            this.panel1.Controls.Add(picbox);
            picbox.BringToFront();
            currentPictureBox = picbox;
            m_currentPictureBoxDoublePageIndex = currentDoublePage;

            // Previent le controleur de la creation d'un pGameObject
            m_editorController.notifyNewPGameObject(library.SelectedIndex, doublePages[currentDoublePage].IndexOf(picbox));
        }

        // OnKeyDown
        private void myKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control)
            {
                if (m_isCtrlKeyPressed != true)
                {
                    m_isCtrlKeyPressed = true;
                }
            }

            if (m_isCtrlKeyPressed && e.KeyCode == Keys.Delete && currentPictureBox != null)
            {
                int indexToRemove = doublePages[currentDoublePage].IndexOf(currentPictureBox);
                doublePages[currentDoublePage].RemoveAt(indexToRemove);
                currentPictureBox.Dispose();
                currentPictureBox = null;

                // Previent le controleur de la suppression d'un pGameObject
                m_editorController.notifyRemoveSelectedPGameObject(indexToRemove);
            }
        }

        // OnKeyUp
        private void myKeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.ControlKey)
            {
                m_isCtrlKeyPressed = false;
            }
        }

        // MouseDown on Panel1
        private void panel1_MouseDown(object sender, MouseEventArgs e)
        {
            if (currentPictureBox != null)
            {
                currentPictureBox.BorderStyle = BorderStyle.None;
                currentPictureBox = null;

                textBoxName.Text = "";
                textBoxX.Text = "";
                textBoxY.Text = "";
                textBoxScaleX.Text = "";
                textBoxScaleY.Text = "";
                textBoxAngle.Text = "";

            }
        }

        // MouseUp on Panel1 / PictureBox
        private void myMouseUp(object sender, MouseEventArgs e)
        {
            isDragging = false;
        }

        // Panel1 / PictureBox MouseLeave 
        private void myMouseLeave(object sender, EventArgs e)
        {
            isDragging = false;
        }

        // MouseDown on PictureBox
        private void pictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            PictureBox picbox = (PictureBox)sender;

            // Si CTRL est appuye c'est l'ajout a liste des objets linkes
            if (m_isCtrlKeyPressed && currentPictureBox != null && currentPictureBox != picbox)
            {
                m_editorController.notifyNewLinkedPGO(doublePages[m_currentPictureBoxDoublePageIndex].IndexOf(currentPictureBox), m_currentPictureBoxDoublePageIndex, doublePages[currentDoublePage].IndexOf(picbox), currentDoublePage);

                // Rafraichissement de la sélection
                m_editorController.notifyPGameObjectSelection(doublePages[m_currentPictureBoxDoublePageIndex].IndexOf(currentPictureBox));
            }
            else
            {
                if (currentPictureBox != null)
                    currentPictureBox.BorderStyle = BorderStyle.None;

                picbox.BorderStyle = BorderStyle.FixedSingle;
                picbox.BringToFront();
                currentPictureBox = picbox;
                m_currentPictureBoxDoublePageIndex = currentDoublePage;

                this.textBoxX.Text = currentPictureBox.Left.ToString();
                this.textBoxY.Text = currentPictureBox.Top.ToString();

                isDragging = true;

                mouseCurrentX = e.X;
                mouseCurrentY = e.Y;

                // Previent le controleur de la selection d'un pGameObject
                m_editorController.notifyPGameObjectSelection(doublePages[currentDoublePage].IndexOf(picbox));
            }
        }

        // Mousemove on PictureBox
        private void pictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            PictureBox picbox = (PictureBox)sender;

            if (isDragging)
            {
                picbox.Left = picbox.Left + e.X - mouseCurrentX;
                picbox.Top = picbox.Top + e.Y - mouseCurrentY;

                this.textBoxX.Text = currentPictureBox.Left.ToString();
                this.textBoxY.Text = currentPictureBox.Top.ToString();
            }
        }

        private void biblioList_SelectedIndexChanged(object sender, EventArgs e)
        {
            m_editorController.notifyLibraryObjectSelection(library.SelectedIndex);
        }

        private void buttonNextDoublePage_Click(object sender, EventArgs e)
        {
            m_editorController.notifySwitchToNextDoublePage();
        }

        private void buttonPreviousDoublePage_Click(object sender, EventArgs e)
        {
            m_editorController.notifySwitchToPreviousDoublePage();
        }

        #endregion

        #region Top bar menu
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog1 = new OpenFileDialog();
            openFileDialog1.InitialDirectory = "..\\..\\xml\\levels";

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                m_editorController.notifyLoadLevel(openFileDialog1.FileName);
            }

            currentLevelSaveUrl = openFileDialog1.FileName;

            this.Text = "Editeur de niveau - " + currentLevelSaveUrl;
        }

        private void newLevelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_editorController.notifyNewLevel();
        }

        private void newDoublePageToolStripMenuItem_Click_1(object sender, EventArgs e)
        {
            m_editorController.notifyNewDoublePage();
        }

        private void removeDoublePageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_editorController.notifyRemoveCurrentDoublePage();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (currentLevelSaveUrl == null)
            {
                saveAsToolStripMenuItem_Click(sender, e);
            }
            else
            {
                m_editorController.notifySaveLevel(currentLevelSaveUrl);
            }
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.InitialDirectory = "..\\..\\xml\\levels";

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                currentLevelSaveUrl = saveFileDialog.FileName;
                m_editorController.notifySaveLevel(currentLevelSaveUrl);
            }

            this.Text = "Editeur de niveau - " + currentLevelSaveUrl;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_editorController.notifiyExit();
        }

        #endregion

        #region Properties boxes
        private void textBoxName_TextChanged(object sender, EventArgs e)
        {
            if (currentPictureBox != null && textBoxName.Text.Length != 0)
            {
                m_editorController.notifyPGameObjectParamChanged(doublePages[currentDoublePage].IndexOf(currentPictureBox), textBoxName.Name, textBoxName.Text);
            }
        }

        private void textBoxX_TextChanged(object sender, EventArgs e)
        {
            if (currentPictureBox != null && textBoxX.Text.Length != 0)
            {
                m_editorController.notifyPGameObjectParamChanged(doublePages[currentDoublePage].IndexOf(currentPictureBox), textBoxX.Name, (int)float.Parse(this.textBoxX.Text.Replace('.', ',')));
            }
        }

        private void textBoxY_TextChanged(object sender, EventArgs e)
        {
            if (currentPictureBox != null && textBoxY.Text.Length != 0)
                m_editorController.notifyPGameObjectParamChanged(doublePages[currentDoublePage].IndexOf(currentPictureBox), textBoxY.Name, (int)float.Parse(this.textBoxY.Text.Replace('.', ',')));
        }

        private void textBoxScaleX_TextChanged(object sender, EventArgs e)
        {
            if (currentPictureBox != null && textBoxScaleX.Text.Length != 0)
                m_editorController.notifyPGameObjectParamChanged(doublePages[currentDoublePage].IndexOf(currentPictureBox), textBoxScaleX.Name, float.Parse(this.textBoxScaleX.Text.Replace('.', ',')));
        }

        private void textBoxScaleY_TextChanged(object sender, EventArgs e)
        {
            if (currentPictureBox != null && textBoxScaleY.Text.Length != 0)
                m_editorController.notifyPGameObjectParamChanged(doublePages[currentDoublePage].IndexOf(currentPictureBox), textBoxScaleY.Name, float.Parse(this.textBoxScaleY.Text.Replace('.', ',')));
        }

        private void textBoxAngle_TextChanged(object sender, EventArgs e)
        {
            if (currentPictureBox != null && textBoxAngle.Text.Length != 0)
                m_editorController.notifyPGameObjectParamChanged(doublePages[currentDoublePage].IndexOf(currentPictureBox), textBoxAngle.Name, float.Parse(this.textBoxAngle.Text.Replace('.', ',')));
        }

        private void listBoxLinkedPGO_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (!m_isCtrlKeyPressed)
            {
                return;
            }

            m_editorController.notifyRemoveLinkedPGO(doublePages[m_currentPictureBoxDoublePageIndex].IndexOf(currentPictureBox), m_currentPictureBoxDoublePageIndex, listBoxLinkedPGO.Items.IndexOf(listBoxLinkedPGO.SelectedItem));
        }

        #endregion

        #region Methodes pour ecouter le modele
        public void onStatutChanged(object sender, StatutChangedEvent e)
        {
            Console.WriteLine(e.eventMsg);
        }

        public void onLibraryChanged(object sender, LibraryChangedEvent e)
        {
            library.DataSource = e.library;
            pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
            pictureBox1.Image = e.bmp;
            Console.WriteLine(e.eventMsg);
        }

        public void onPGameObjectChanged(object sender, PGameObjectChangedEvent e)
        {
            currentPictureBox.Left = (int)e.pGameObject.x;
            currentPictureBox.Top = (int)e.pGameObject.y;

            if (!isDragging)
            {
                currentPictureBox.Image = (Bitmap)e.pGameObject.getBmp.Clone();

                float angle = e.pGameObject.orientationAngle;
                if (angle != 0)
                    currentPictureBox.Image = RotateImg((Bitmap)currentPictureBox.Image, angle, Color.Transparent);

                float scaleX = e.pGameObject.scaleX;
                float scaleY = e.pGameObject.scaleY;
                if (scaleX != 0 && scaleY != 0)
                    currentPictureBox.Image = (Bitmap)currentPictureBox.Image.GetThumbnailImage((int)(currentPictureBox.Width * scaleX), (int)(currentPictureBox.Height * scaleY), null, IntPtr.Zero);
            }
        }

        public void onPGameObjectSelected(object sender, PGameObjectSelectedEvent e)
        {
            textBoxName.Text = e.pGameObject.name;
            textBoxX.Text = e.pGameObject.x.ToString();
            textBoxY.Text = e.pGameObject.y.ToString();
            textBoxScaleX.Text = e.pGameObject.scaleX.ToString();
            textBoxScaleY.Text = e.pGameObject.scaleY.ToString();
            textBoxAngle.Text = e.pGameObject.orientationAngle.ToString();
            checkBoxIsActivate.Checked = e.pGameObject.isActive;
            listBoxLinkedPGO.Items.Clear();

            if (e.pGameObject.linkedPGOList != null)
            {
                for (int i = 0; i < e.pGameObject.linkedPGOList.Count; ++i)
                {
                    listBoxLinkedPGO.Items.Add(e.pGameObject.linkedPGOList[i].name);
                }

                listBoxLinkedPGO.BackColor = Color.White;
                listBoxLinkedPGO.Enabled = true;
            }
            else
            {
                listBoxLinkedPGO.BackColor = Color.Gray;
                listBoxLinkedPGO.Enabled = false;
            }

            Dictionary<string, string> data = e.pGameObject.getViewData();

            if (data.ContainsKey("masse"))
            {
                textBoxMasse.Text = data["masse"];
            }
            else
            {
                textBoxMasse.BackColor = Color.Gray;
                textBoxMasse.Enabled = false;
            }

            if (data.ContainsKey("straightness"))
            {
                textBoxRaideur.Text = data["straightness"];
            }
            else
            {
                textBoxRaideur.BackColor = Color.Gray;
                textBoxRaideur.Enabled = false;
            }

            if (data.ContainsKey("tackiness"))
            {
                textBoxViscosite.Text = data["tackiness"];
            }
            else
            {
                textBoxViscosite.BackColor = Color.Gray;
                textBoxViscosite.Enabled = false;
            }

            if (data.ContainsKey("deadening"))
            {
                textBoxAmortissement.Text = data["deadening"];
            }
            else
            {
                textBoxAmortissement.BackColor = Color.Gray;
                textBoxAmortissement.Enabled = false;
            }
        }

        public void onDoublePageChanged(object sender, DoublePageChangedEvent e)
        {
            // New DP
            if (e.changedType == 0)
            {
                doublePages.Add(new List<PictureBox>());
            }

            for (int i = 0; i < doublePages[currentDoublePage].Count; i++)
                doublePages[currentDoublePage][i].Visible = false;

            // Delete DP
            if (e.changedType == 2)
            {
                int numberOfPictureBox = doublePages[currentDoublePage].Count;
                for (int i = numberOfPictureBox - 1; i >= 0; i--)
                    doublePages[currentDoublePage][i].Dispose();
                doublePages[currentDoublePage].Clear();
                doublePages.RemoveAt(currentDoublePage);
            }

            currentDoublePage = e.pageIndex;

            for (int i = 0; i < doublePages[currentDoublePage].Count; i++)
                doublePages[currentDoublePage][i].Visible = true;

            textBoxDoublePageNum.Text = (e.pageIndex + 1) + " / " + e.levelPagesNb;
        }

        public void onLevelChanged(object sender, LevelChangedEvent e)
        {
            // Refresh
            for (int i = doublePages.Count - 1; i >= 0; i--)
            {
                for (int j = doublePages[i].Count - 1; j >= 0; j--)
                    doublePages[i][j].Dispose();

                doublePages[i].Clear();
                doublePages.RemoveAt(i);
            }

            // New level
            if (e.type == 0)
            {
                currentLevelSaveUrl = null;
                this.Text = "Editeur de niveau - Nouveau niveau";
            }

            // Chargements
            if (e.type == 1)
            {
                for (int i = 0; i < e.doublePagesNb; i++)
                {
                    doublePages.Add(new List<PictureBox>());

                    for (int j = 0; j < e.doublePagesList[i].pGameObjectsNb; j++)
                    {
                        PictureBox picbox = new PictureBox();
                        picbox.SizeMode = PictureBoxSizeMode.AutoSize;
                        picbox.BackColor = Color.Transparent;
                        picbox.Cursor = Cursors.Hand;

                        picbox.Image = (Bitmap)e.doublePagesList[i].pGameObjectsList[j].getBmp.Clone();
                        picbox.Left = (int)e.doublePagesList[i].pGameObjectsList[j].x;
                        picbox.Top = (int)e.doublePagesList[i].pGameObjectsList[j].y;
                        if (e.doublePagesList[i].pGameObjectsList[j].orientationAngle != 0)
                            picbox.Image = RotateImg((Bitmap)picbox.Image, e.doublePagesList[i].pGameObjectsList[j].orientationAngle, Color.Transparent);
                        picbox.Image = (Bitmap)picbox.Image.GetThumbnailImage((int)(picbox.Width * e.doublePagesList[i].pGameObjectsList[j].scaleX), (int)(picbox.Height * e.doublePagesList[i].pGameObjectsList[j].scaleY), null, IntPtr.Zero);

                        picbox.MouseDown += new MouseEventHandler(this.pictureBox_MouseDown);
                        picbox.MouseMove += new MouseEventHandler(this.pictureBox_MouseMove);
                        picbox.MouseUp += new MouseEventHandler(this.myMouseUp);
                        picbox.MouseLeave += new EventHandler(this.myMouseLeave);

                        doublePages[i].Add(picbox);
                        this.panel1.Controls.Add(picbox);

                        // Affichage de la double page d'indice 0
                        if (i == 0)
                            picbox.Visible = true;
                        else
                            picbox.Visible = false;
                    }
                }

                currentDoublePage = 0;
            }

            textBoxDoublePageNum.Text = "1 / " + e.doublePagesNb;
            currentPictureBox = null;
            Console.WriteLine(e.eventMsg);

            textBoxName.Text = "";
            textBoxX.Text = "";
            textBoxY.Text = "";
            textBoxScaleX.Text = "";
            textBoxScaleY.Text = "";
            textBoxAngle.Text = "";
        }

        #endregion
    }
}
